package server

import (
	"github.com/gorilla/websocket"
	"net/http"
	"adai.design/homemaster/log"
	"encoding/json"
)

type Client struct {
	s *Server
	conn *websocket.Conn
	send chan []byte
}

func (c *Client) readMessage() {
	defer func() {
		c.s.unregister <- c
		c.conn.Close()
	}()

	for {
		_, message, err := c.conn.ReadMessage()
		if err != nil {
			//log.Println("read err: ", err)
			break
		}
		var msg Message
		err = json.Unmarshal(message, &msg)
		if err == nil {
			if controller, ok := messageHandleMap[msg.Path]; ok {
				err = controller.Handle(c.conn, &msg)
				if err != nil {
					log.Error("%s", err)
				}
			}
		} else {
			log.Debug("recv: %s", message)
		}
	}
}

func (c *Client) executeTask()  {
	defer func() {
		c.conn.Close()
	}()

	for {
		select {
		case msg, ok := <-c.send:
			if !ok {
				return
			}
			err := c.conn.WriteMessage(websocket.TextMessage, msg)
			if err != nil {
				return
			}
		}
	}
}

type Server struct {
	clients map[*Client]bool

	notify chan []byte
	register chan *Client
	unregister chan *Client
}

func (s *Server) broadcast(msg []byte) error {
	s.notify <- msg
	return nil
}

func newServer() *Server {
	return &Server{
		clients:    make(map[*Client]bool),
		notify:     make(chan []byte),
		register:   make(chan *Client),
		unregister: make(chan *Client),
	}
}

func (s *Server) run() {
	for {
		select {
		case client := <- s.register:
			s.clients[client] = true
			log.Debug("arrive: %s", client.conn.RemoteAddr())

		case client := <-s.unregister:
			if _, ok := s.clients[client]; ok {
				log.Debug("leave: %s", client.conn.RemoteAddr())
				delete(s.clients, client)
				close(client.send)
			}

		case notify := <-s.notify:
			for client := range s.clients {
				select {
				case client.send <- notify:
				default:
					log.Debug("leave: %s", client.conn.RemoteAddr())
					close(client.send)
					delete(s.clients, client)
				}
			}
		}
	}
}

var upgrader = websocket.Upgrader{}

func serveWs(s *Server, w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("upgrade: ", err)
		return
	}

	client := &Client{s: s, conn: c, send: make(chan []byte, 5)}
	client.s.register <- client

	go client.readMessage()
	go client.executeTask()
}